/*
 * Famon (http://code.google.com/p/famon/)
 * Copyright (c) 2010 Carl Crous.
 *
 * This file is part of Famon.
 *
 * Famon is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Famon is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Famon.  If not, see <http://www.gnu.org/licenses/>.
 */

package za.org.foobar.famon.frontend.client.ui;

import com.smartgwt.client.data.Record;
import com.smartgwt.client.widgets.Canvas;
import com.smartgwt.client.widgets.form.DynamicForm;
import com.smartgwt.client.widgets.form.fields.SelectItem;
import com.smartgwt.client.widgets.form.fields.events.ChangeEvent;
import com.smartgwt.client.widgets.form.fields.events.ChangeHandler;
import com.smartgwt.client.widgets.form.fields.events.DataArrivedEvent;
import com.smartgwt.client.widgets.form.fields.events.DataArrivedHandler;
import com.smartgwt.client.widgets.grid.CellFormatter;
import com.smartgwt.client.widgets.grid.ListGrid;
import com.smartgwt.client.widgets.grid.ListGridField;
import com.smartgwt.client.widgets.grid.ListGridRecord;
import com.smartgwt.client.widgets.layout.VStack;
import com.smartgwt.client.widgets.viewer.DetailViewer;
import com.smartgwt.client.widgets.viewer.DetailViewerField;
import org.gwtportlets.portlet.smartgwt.client.SmartPortlet;
import za.org.foobar.famon.frontend.client.dto.FailureDto;
import za.org.foobar.famon.frontend.client.dto.LoggingEventDto;
import za.org.foobar.famon.frontend.client.dto.StackTraceElementDto;
import za.org.foobar.famon.frontend.client.dto.ThreadDto;
import za.org.foobar.famon.frontend.client.util.SmartGwtUtil;

/**
 *
 */
public class FailureWidget extends VStack {
    private final SmartPortlet portlet;
    public static final String COMPONENT_ID_PREFIX = "failureWidget";
    public static final String THREAD_PANEL_ID_PREFIX = "threadExpanded";
    private DetailViewer details;
    private Canvas threadPanel;

    public static String getComponentId(String failureId) {
        return COMPONENT_ID_PREFIX + failureId;
    }

    public FailureWidget(SmartPortlet portlet, final String failureId) {
        super(5);
        setID(getComponentId(failureId));
        setWidth100();
        setHeight(480);
        this.portlet = portlet;

        details = new DetailViewer();
        details.setDataSource(FailureDto.getDataSource(portlet));
        details.setShowEmptyField(true);
        details.setAutoFetchData(true);
        details.setInitialCriteria(SmartGwtUtil.getCriteria("id", failureId));

        DetailViewerField failureType = new DetailViewerField("failureType", "Type");
        DetailViewerField date = new DetailViewerField("date", "Date");
        DetailViewerField message = new DetailViewerField("message", "Message");
        DetailViewerField sourceHostname = new DetailViewerField("sourceHostname", "Source Hostname");
        DetailViewerField sourceAddress = new DetailViewerField("sourceAddress", "Source Address");
        DetailViewerField sourcePort = new DetailViewerField("sourcePort", "Source Port");
        DetailViewerField originalSourceHostname = new DetailViewerField("originalSourceHostname", "Original Source Hostname");
        DetailViewerField originalSourceAddress = new DetailViewerField("originalSourceAddress", "Original Source Address");
        DetailViewerField user = new DetailViewerField("user", "User");
        details.setFields(failureType, date, message, sourceHostname, sourceAddress, sourcePort, originalSourceHostname,
                originalSourceAddress, user);
        addMember(details);

        final DynamicForm threadForm = new DynamicForm();
        final SelectItem threadSelect = new SelectItem();
        threadSelect.setDefaultToFirstOption(true);
        threadSelect.setTitle("Thread");
        threadSelect.setValueField("id");
        threadSelect.setDisplayField("name");
        threadSelect.setPickListWidth(250);
        threadSelect.setOptionDataSource(ThreadDto.getDataSource(portlet));
        threadSelect.setOptionCriteria(SmartGwtUtil.getCriteria("failureId", failureId));
        threadSelect.addChangeHandler(new ChangeHandler() {
            public void onChange(ChangeEvent event) {
                showThreadPanel(failureId, (String)event.getValue());
            }
        });
        threadSelect.addDataArrivedHandler(new DataArrivedHandler() {
            public void onDataArrived(DataArrivedEvent event) {
                if (threadPanel == null) {
                    Record record = threadSelect.getSelectedRecord();
                    if (record == null) {
                        return;
                    }
                    String id = record.getAttribute("id");
                    if (id == null) {
                        return;
                    }
                    showThreadPanel(failureId, id);
                }
            }
        });
        threadForm.setFields(threadSelect);
        addMember(threadForm);
    }

    private void showThreadPanel(String failureId, String threadId) {
        if (threadPanel != null && hasMember(threadPanel)) {
            removeChild(threadPanel);
        }
        threadPanel = getThreadPanel(failureId, threadId);
        addMember(threadPanel);
    }
    
    private static String getThreadComponentId(String failureId, String threadId) {
        return THREAD_PANEL_ID_PREFIX + "_" + failureId + "_" + threadId;
    }

    private Canvas getThreadPanel(String failureId, String threadId) {
        String componentId = getThreadComponentId(failureId, threadId);
        Canvas c = Canvas.getById(componentId);
        if (c != null) {
            return c;
        }
        return new ThreadPanel(componentId, failureId, threadId);
    }

    public class ThreadPanel extends VStack {

        public ThreadPanel(String componentId, String failureId, String threadId) {
            super(5);
            setID(componentId);
            addMember(new StackTraceListGrid(failureId, threadId));
            addMember(new LoggingEventListGrid(failureId, threadId));
        }
    }

    public class StackTraceListGrid extends ListGrid {
        public StackTraceListGrid(String failureId, String threadId) {
            setDataSource(StackTraceElementDto.getDataSource(portlet));
            setAutoFetchData(true);
            setInitialCriteria(SmartGwtUtil.getCriteria("threadId", threadId, "failureId", failureId));
            setWidth100();
            setHeight(200);
            ListGridField index = new ListGridField("index", "Index", 50);
            ListGridField stackTrace = new ListGridField("stackTraceElement", "Stack Trace");
            stackTrace.setCellFormatter(new CellFormatter() {
                public String format(Object value, ListGridRecord r, int rowNum, int colNum) {
                    return r.getAttribute("className") + "." + r.getAttribute("methodName") +
                            "(" + r.getAttribute("fileName") + ":" + r.getAttribute("lineNumber") + ")";
                }
            });
            ListGridField fileName = new ListGridField("fileName", "File");
            fileName.setHidden(true);
            ListGridField className = new ListGridField("className", "Class");
            className.setHidden(true);
            ListGridField methodName = new ListGridField("methodName", "Method");
            methodName.setHidden(true);
            ListGridField lineNumber = new ListGridField("lineNumber", "Line");
            lineNumber.setHidden(true);
            setFields(index, stackTrace, fileName, className, methodName, lineNumber);
        }
    }

    public class LoggingEventListGrid extends ListGrid {
        public LoggingEventListGrid(String failureId, String threadId) {
            setDataSource(LoggingEventDto.getDataSource(portlet));
            setAutoFetchData(true);
            setInitialCriteria(SmartGwtUtil.getCriteria("threadId", threadId, "failureId", failureId));
            setWidth100();
            setHeight(120);
            ListGridField index = new ListGridField("index", "Index", 50);
            ListGridField level = new ListGridField("level", "Level", 80);
            ListGridField loggerName = new ListGridField("loggerName", "Logger");
            ListGridField message = new ListGridField("message", "Message");
            ListGridField stackTrace = new ListGridField("stackTraceElement", "Stack Trace");
            stackTrace.setCellFormatter(new CellFormatter() {
                public String format(Object value, ListGridRecord r, int rowNum, int colNum) {
                    return r.getAttribute("className") + "." + r.getAttribute("methodName") +
                            "(" + r.getAttribute("fileName") + ":" + r.getAttribute("lineNumber") + ")";
                }
            });
            ListGridField fileName = new ListGridField("fileName", "File");
            fileName.setHidden(true);
            ListGridField className = new ListGridField("className", "Class");
            className.setHidden(true);
            ListGridField methodName = new ListGridField("methodName", "Method");
            methodName.setHidden(true);
            ListGridField lineNumber = new ListGridField("lineNumber", "Line");
            lineNumber.setHidden(true);
            setFields(index, level, loggerName, message, stackTrace, fileName, className, methodName, lineNumber);
        }
    }
}
